package icu.guodapeng.gateway.config

import icu.guodapeng.gateway.component.AuthorizationManager
import icu.guodapeng.gateway.component.RestAuthenticationEntryPoint
import icu.guodapeng.gateway.component.RestfulAccessDeniedHandler
import icu.guodapeng.gateway.domain.user.enum.AuthConstant
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.convert.converter.Converter
import org.springframework.security.authentication.AbstractAuthenticationToken
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.oauth2.jwt.Jwt
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter
import org.springframework.security.web.server.SecurityWebFilterChain
import reactor.core.publisher.Mono
import javax.annotation.Resource

/**
 * 资源服务器配置
 */
@Configuration
@EnableWebFluxSecurity
class ResourceServerConfig {

    @Resource
    lateinit var ignoreUrlsConfig: IgnoreUrlsConfig

    @Resource
    lateinit var authorizationManager: AuthorizationManager

    @Resource
    lateinit var restfulAccessDeniedHandler: RestfulAccessDeniedHandler

    @Resource
    lateinit var restAuthenticationEntryPoint: RestAuthenticationEntryPoint

    @Bean
    fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
        http.oauth2ResourceServer().jwt().jwtAuthenticationConverter(jwtAuthenticationConverter())
        // 自定义处理JWT请求头过期或签名错误的结果
        http.oauth2ResourceServer().authenticationEntryPoint(restAuthenticationEntryPoint)
        http.authorizeExchange()
            .pathMatchers(*(ignoreUrlsConfig.urls.toTypedArray())).permitAll() // 白名单配置
            .anyExchange().access(authorizationManager) // 鉴权管理器配置
            .and()
            .exceptionHandling()
            .accessDeniedHandler(restfulAccessDeniedHandler) // 处理未授权
            .authenticationEntryPoint(restAuthenticationEntryPoint) // 处理未认证
            .and()
            .csrf().disable()

        return http.build()
    }

    @Bean
    fun jwtAuthenticationConverter(): Converter<Jwt, out Mono<out AbstractAuthenticationToken>> {
        val jwtGrantedAuthoritiesConverter = JwtGrantedAuthoritiesConverter()
        jwtGrantedAuthoritiesConverter.setAuthorityPrefix(AuthConstant.AUTHORITY_PREFIX.value)
        jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName(AuthConstant.AUTHORITY_CLAIM_NAME.value)
        val jwtAuthenticationConverter = JwtAuthenticationConverter()
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter)
        return ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter)
    }
}
